package io.hellsinger.vortex.ui.component.storage

import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import io.hellsinger.theme.vortex.VortexSettings
import io.hellsinger.vortex.bit.Bits.Companion.addFlag
import io.hellsinger.vortex.bit.Bits.Companion.removeFlag
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.data.model.name
import io.hellsinger.vortex.data.repository.AndroidStorageRepository
import io.hellsinger.vortex.foundation.isAtLeastAndroid11
import io.hellsinger.vortex.ui.attachJob
import io.hellsinger.vortex.ui.component.adapter.ItemViewProvider
import io.hellsinger.vortex.ui.component.adapter.SingleTypeAdapter
import io.hellsinger.vortex.ui.component.adapter.listener.ItemListener
import io.hellsinger.vortex.ui.component.storage.standard.linear.StandardStorageLinearCell
import io.hellsinger.vortex.util.DefaultPathItemDescriptor
import io.hellsinger.vortex.util.icon
import kotlinx.coroutines.async
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class StorageAdapter(
    private val listener: ItemListener,
    private val isSelected: (PathItem) -> Boolean = { false },
    private val viewProvider: ItemViewProvider<StandardStorageLinearCell> =
        ItemViewProvider { parent, _ ->
            StandardStorageLinearCell(
                parent.context,
            )
        },
) : SingleTypeAdapter<PathItem, StorageViewHolder>() {
    private val descriptor = DefaultPathItemDescriptor()
    private val mutex = Mutex()
    private val clickListenerWrapper =
        OnClickListener { view ->
            listener.onItemClick(view, view.tag as Int)
        }

    private val longClickListenerWrapper =
        OnLongClickListener { view ->
            return@OnLongClickListener listener.onLongItemClick(view, view.tag as Int)
        }

    override fun createDiffer(new: List<PathItem>): DiffUtil.Callback = Differ(list, new)

    override fun getItemId(position: Int): Long = list[position].id

    override fun createViewHolder(parent: ViewGroup): StorageViewHolder = StorageViewHolder(viewProvider(parent))

    private fun update(
        position: Int,
        type: Int,
    ) {
        notifyItemChanged(position, type)
    }

    fun applySelect(position: Int) = update(position, ADD_SELECTION)

    fun applyDeselect(position: Int) = update(position, REMOVE_SELECTION)

    fun clearSelection() {
        for (i in list.indices) update(i, REMOVE_SELECTION)
    }

    override fun onBindViewHolder(
        holder: StorageViewHolder,
        position: Int,
    ) {
        val item = get(position)

        with(holder.itemView as StandardStorageLinearCell) {
            title = item.name
            icon = item.icon
            tag = holder.bindingAdapterPosition
            if (isSelected(item)) {
                flag =
                    flag addFlag UPDATE_ITEM_SELECTION addFlag
                    UPDATE_WITHOUT_ANIMATION
            }

            attachJob(scope) {
                if (isAtLeastAndroid11) {
                    val isObb = item.path == AndroidStorageRepository.OBB_DIRECTORY_PATH
                    val isData = item.path == AndroidStorageRepository.DATA_DIRECTORY_PATH
                    if (isObb || isData) {
                        subtitle = VortexSettings.texts { key_storage_list_item_description_restricted }
                        return@attachJob
                    }
                }

                if (item.description == null) {
                    subtitle =
                        VortexSettings.texts { key_storage_list_item_description_placeholder }
                    item.description =
                        async {
                            /**
                             * Synchronization
                             **/
                            mutex.withLock {
                                descriptor.describe(item)
                            }
                        }
                    animateInfoUpdate(item.description?.await())
                } else {
                    /**
                     *  Don't need smooth update animation
                     *  Most time will be instantly awaited with seamless effect
                     **/
                    subtitle = item.description?.await()
                }
            }
        }
    }

    override fun onViewAttachedToWindow(holder: StorageViewHolder) {
        holder.root.onBindListener(clickListenerWrapper)
        holder.root.onBindLongListener(longClickListenerWrapper)
    }

    override fun onViewDetachedFromWindow(holder: StorageViewHolder) {
        holder.itemView.setOnClickListener(null)
        holder.itemView.setOnLongClickListener(null)
    }

    override fun onViewRecycled(holder: StorageViewHolder) {
        with(holder.itemView as StandardStorageLinearCell) {
            title = null
            icon = null
            subtitle = null
            flag = 0
        }
    }

    override fun onBindViewHolder(
        holder: StorageViewHolder,
        position: Int,
        payloads: MutableList<Any>,
    ) {
        if (payloads.isEmpty()) return onBindViewHolder(holder, position)
        payloads.onEach { payload ->
            if (payload !is Int) return
            with(holder.itemView as StandardStorageLinearCell) {
                when (payload) {
                    ADD_SELECTION -> flag = flag addFlag UPDATE_ITEM_SELECTION
                    REMOVE_SELECTION -> flag = flag removeFlag UPDATE_ITEM_SELECTION
                }
            }
        }
    }

    class Differ(
        private val old: List<PathItem>,
        private val new: List<PathItem>,
    ) : DiffUtil.Callback() {
        override fun getOldListSize(): Int = old.size

        override fun getNewListSize(): Int = new.size

        override fun areItemsTheSame(
            oldItemPosition: Int,
            newItemPosition: Int,
        ) = old[oldItemPosition].id == new[newItemPosition].id

        override fun areContentsTheSame(
            oldItemPosition: Int,
            newItemPosition: Int,
        ): Boolean {
            val old = old[oldItemPosition]
            val new = new[newItemPosition]

            return old == new
        }
    }

    init {
        setHasStableIds(true)
    }

    companion object {
        const val ADD_SELECTION = 1
        const val REMOVE_SELECTION = 2

        const val UPDATE_ITEM_SELECTION = 1
        const val UPDATE_WITHOUT_ANIMATION = 4
    }
}
